/** sps_sign_req.cpp - implemention of using usbkey sign
 */

#include "sps_config.h"

#define SJN_PIN                        "pin"
#define SJN_SERIAL_CODE                "serialCode"
#define SJN_HASH_DATE                  "hashDate"
#define SJN_HASH_DATA                  "hashData"
#define SJN_ESTIMATED_SIGN_SIZE        "estimatedSignSize"
#define SJN_SIGN_DATE                  "signDate"
#define SJN_SIGN_ALGORITHM             "signAlgorithm"
#define SJN_SIGN_DATA                  "signData"

SpsSignReq::SpsSignReq(const std::string& ip, const std::string& mac)
  : SpsReq(ip, mac)
{
}

SpsSignReq::~SpsSignReq()
{
}

std::string SpsSignReq::getResponse()
{
  if (err_ != ESPS_SUCCESS) {
    return SpsReq::getResponse();
  }

  json v;
  
  v[SJN_TYPE] = 1;
  v[SJN_CODE] = code_;
  v[SJN_RESULT_STATUS_CODE] = getResultStatusCode();
  v[SJN_RESULT_CODE] = getResultCode();
  v[SJN_RESULT_DESC] = getResultDesc();
  v[SJN_SESSION_ID] = session_;
  v[SJN_AGENT_CLIENT_IP] = local_ip_;
  v[SJN_ALGORITHM] = msgalgo_;

  json d;
  d[SJN_SERIAL_CODE] = serial_;
  d[SJN_HASH_DATE] = hashdate_;
  d[SJN_ESTIMATED_SIGN_SIZE] = estimated_;
  d[SJN_SIGN_DATE] = signdate_;
  d[SJN_SIGN_ALGORITHM] = signalgo_;
  d[SJN_SIGN_DATA] = Base64Encode(signdata_);
  
  v[SJN_RESULT_DATA] = d;
  
  v[SJN_SIGNATURE] = generateVerify(v);

  return v.to_string();
}

/*******************************************************************************
 * Protected functions                                                         *
 ******************************************************************************/

int SpsSignReq::load()
{
  int ret;

  ret = SpsReq::load();
  if (ret != ESPS_SUCCESS) {
    return ret;
  }

  try {
    json d = req_[SJN_SEND_DATA];
    pin_ = d[SJN_PIN].as_string();
    serial_ = d[SJN_SERIAL_CODE].as_string();
    hashdata_ = Base64Decode(d[SJN_HASH_DATA].as_string());
    hashdate_ = d[SJN_HASH_DATE].as_ulong();
    estimated_ = d[SJN_ESTIMATED_SIGN_SIZE].as_ulong();
  } catch (const json_exception& e) {
    SpsLog::instance().logError("load request from json failed (%s)", e.what());
    return ESPS_PARSE_FAILED;
  }

  return ESPS_SUCCESS;
}

int SpsSignReq::verify()
{
  std::list<std::string> l;

  l.push_back(req_[SJN_TYPE].as_string());
  l.push_back(req_[SJN_CODE].as_string());
  l.push_back(req_[SJN_SESSION_ID].as_string());
  l.push_back(req_[SJN_AGENT_CLIENT_IP].as_string());
  l.push_back(req_[SJN_ALGORITHM].as_string());

  json d = req_[SJN_SEND_DATA];

  l.push_back(d[SJN_PIN].as_string());
  l.push_back(d[SJN_SERIAL_CODE].as_string());
  l.push_back(d[SJN_HASH_DATE].as_string());
  l.push_back(d[SJN_ESTIMATED_SIGN_SIZE].as_string());
  l.push_back(d[SJN_HASH_DATA].as_string());
 
  l.push_back(sps_priv_key);

  l.sort();

  std::string msg;
  std::list<std::string>::const_iterator i;
  for (i = l.begin(); i != l.end(); i++) {
    msg += (*i + "&");
  }

  msg.erase(msg.end() - 1);
  
  return checkRequest(msg);
}

int SpsSignReq::doit()
{
  /*
   * TODO: Use usbkey sign.
   */

  /* NOTE: This is a test. */
  signalgo_ = "RSA";
  signdate_ = time(NULL);
  signdata_ = "hello world";
  
  return ESPS_SUCCESS;
}

std::string SpsSignReq::generateVerify(const json& v)
{
  std::list<std::string> l;

  l.push_back(v[SJN_TYPE].as_string());
  l.push_back(v[SJN_CODE].as_string());
  l.push_back(v[SJN_RESULT_STATUS_CODE].as_string());
  l.push_back(v[SJN_RESULT_CODE].as_string());
  l.push_back(v[SJN_RESULT_DESC].as_string());
  l.push_back(v[SJN_SESSION_ID].as_string());
  l.push_back(v[SJN_AGENT_CLIENT_IP].as_string());
  l.push_back(v[SJN_ALGORITHM].as_string());

  json d = v[SJN_RESULT_DATA];
  l.push_back(d[SJN_SERIAL_CODE].as_string());
  l.push_back(d[SJN_HASH_DATE].as_string());
  l.push_back(d[SJN_ESTIMATED_SIGN_SIZE].as_string());
  l.push_back(d[SJN_SIGN_DATE].as_string());
  l.push_back(d[SJN_SIGN_ALGORITHM].as_string());
  l.push_back(d[SJN_SIGN_DATA].as_string());

  l.push_back(sps_priv_key);

  l.sort();

  std::string msg;
  std::list<std::string>::const_iterator i;
  for (i = l.begin(); i != l.end(); i++) {
    msg += (*i + "&");
  }

  msg.erase(msg.end() - 1);

  return getHash(msg);
}
